package cn.imatu.framework.log;

import cn.imatu.framework.log.annotation.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 操作注解扫描
 *
 * @author shenguangyang
 */
@Slf4j
public class OperateLogAnnotScan implements InitializingBean, ApplicationContextAware,
        ApplicationListener<ApplicationStartedEvent> {
    private ApplicationContext applicationContext;

    public static final Map<LogEvent, List<OperateLog>> allLogAnnotMap = new HashMap<>();

    @Override
    public void afterPropertiesSet()  {
        log.info("[日志注解扫描] 开始");
        StopWatch stopWatch = StopWatch.createStarted();
        Map<String, RequestMappingHandlerMapping> beansOfType = applicationContext.getBeansOfType(RequestMappingHandlerMapping.class);
        beansOfType.values().forEach(e -> {
            Map<RequestMappingInfo, HandlerMethod> map = e.getHandlerMethods();
            map.keySet().forEach(info -> {
                HandlerMethod handlerMethod = map.get(info);
                OperateLog operateLog = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), OperateLog.class);
                LogEvent logEvent = handlerMethod.getBeanType().getAnnotation(LogEvent.class);
                if (operateLog != null && logEvent == null) {
                    log.warn("[日志注解扫描] 类 {} 不存在 {} 注解", handlerMethod.getBeanType().getSimpleName(),  LogEvent.class.getName());
                    return;
                }
                if (Objects.isNull(logEvent)) {
                    return;
                }

                List<OperateLog> operateLogList = allLogAnnotMap.computeIfAbsent(logEvent, (k) -> new ArrayList<>());
                if (operateLog != null) {
                    operateLogList.add(operateLog);
                }
            });
        });
        log.info("[日志注解扫描] 结束, time: {} ms", stopWatch.getTime(TimeUnit.MILLISECONDS));
        stopWatch.stop();
    }

    @Override
    @SuppressWarnings("all")
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    @SuppressWarnings("all")
    public void onApplicationEvent(ApplicationStartedEvent event) {
    }
}
